/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.tamagochi.bean;

import br.com.tamagochi.view.ViewObserve;
import java.awt.Image;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;

/**
 * Esta Classe é uma representação de um bicho virtural.
 *
 * @author Allan de Lemos Domingos
 * @author Kamilla Cardoso
 */
public class Tamagochi {

    //Definindo constantes de valores pré-definidos nas propriedade
    private final int MIN_VALUES;
    private final int MAX_VALUES;
    private final int NIVEL_MIN;
    private final int NIVEL_MAX;
    private final long ANIVER_MILISEGUNDOS;
    private final int IDADE_MAX;
    
    private int sono;
    private int felicidade;
    private int fome;
    private long idade;
    private int peso;
    private int saude;
    
    private Estados estado;
    
    private boolean alterar;
    
    private List<ViewObserve> observadores;
    private List<List<Image>> images;


    /*
     * Enum correspondente aos estados do bicho virtual
     */    
    public static enum Estados {NORMAL,SONO,FOME,FELIZ,TRISTE,DORMIR,SONO_FOME,SAUDE_CRITICA,COMER,BRINCAR_ESQUERDA,BRINCAR_DIREITA}
    
    /**
     *
     * Construtor do tamagochi
     *
     * @param propriedades
     * @param Lista de imagens ".gif" que ira identificar a situação do bicho
     * virtual
     */
    public Tamagochi(Properties propriedades){
        this.NIVEL_MIN = Integer.parseInt(propriedades.getProperty("nivel.min")); 
        this.NIVEL_MAX = Integer.parseInt(propriedades.getProperty("nivel.max"));
        this.sono = Integer.parseInt((String)propriedades.get("sono"));
        this.saude = Integer.parseInt((String)propriedades.get("saude"));
        this.fome = Integer.parseInt((String)propriedades.get("fome"));
        this.peso = Integer.parseInt((String)propriedades.get("peso"));
        this.idade = Long.parseLong((String)propriedades.get("idade"));
        this.felicidade = Integer.parseInt((String)propriedades.get("felicidade"));
        this.MIN_VALUES = Integer.parseInt((String)propriedades.get("min.values"));
        this.MAX_VALUES = Integer.parseInt((String)propriedades.get("max.values"));
        this.ANIVER_MILISEGUNDOS = Long.parseLong((String)propriedades.get("aniversario.milisegundos"));
        this.IDADE_MAX = Integer.parseInt((String)propriedades.get("idade.max"));
        this.images = (List<List<Image>>) propriedades.get("lista.imagens");
        
        this.estado = Estados.FELIZ;
        
        this.alterar = true;
        
        //inicializa a lista de observadores.
        this.observadores = new LinkedList<ViewObserve>();

        notifyValues();
        notifyImage();
    }

    /**
     * Este metodo tem por objetivo retorna a idade do bicho virtual calculando
     * com em milisegundos e a cada priodo de tempo ele fará aniversário.
     *
     * @return retona a idade do bicho virtual.
     */
    public int getIdade(){
        return (int)((idade / ANIVER_MILISEGUNDOS) + 1 );
    }

    /**
     * Esse metodo define o tempo de vida do bicho virtual em milisegundos
     *
     * @param milisegundos
     */
    public void addTempoDeVida(long milisegundos) {
        int idadeAnterior = getIdade();
        if(this.getIdade() < IDADE_MAX){
            this.idade += milisegundos;
            if(getIdade() > idadeAnterior){
                notifyImage();
            }
        }
    }

    /**
     * Metodo para obter o peso do bicho virtual.
     *
     * @return retorna o peso.
     */
    public int getPeso() {
        return peso;
    }

    /**
     * Metodo para atualizar o nivel da saude do bicho virtual para mais ou para
     * menos conforme o niveis de felicidade, sono e fome.
     *
     * @param und
     */
    public void atualizaSaude(int und) {
        this.saude += und;
        
        if(saude > MAX_VALUES){
            this.saude = MAX_VALUES;
        }
            
        if(this.saude < MIN_VALUES){
            this.saude = MIN_VALUES;
        }
    }

    /**
     * Metodo que ira atualizar os estados sono, fome, felicidade do bicho virtual.
     * 
     * @param und
     */
    public void atualizaValores(int und) {

        this.sono += und;
        if(this.sono > MAX_VALUES){
            this.sono = MAX_VALUES;
        }

        this.fome += und;
        if(this.fome > MAX_VALUES){
            this.fome = MAX_VALUES;
        }
        
        this.felicidade -= und;
        if(this.felicidade < MIN_VALUES )
            this.felicidade = MIN_VALUES;
        
        notifyValues();
        notifyMinMax();
    }
    

    /**
     * Metodo para atualizar o nivel de felicidade do bicho virtual para mais ou
     * para menos.
     *
     * @param und
     */
    public void atualizaFelicidade(int und) {
        this.felicidade += und;
    }

    /**
     * Metodo para atualizar o nivel de fome do bicho virtual para mais ou para
     * menos.
     *
     * @param und
     */
    public void atualizaFome(int und) {
        this.fome += und;
    }

    /**
     * Metodo para obter o nivel de fome do bicho virtual
     *
     * @return int
     */
    public int getFome() {
        return fome;
    }

    /**
     * Metodo para obter uma imagem a partir de seus estados ja definidos no
     * enum Estados, através da sua idade atual
     *
     * @see Enum
     * @return Image
     */
    public Image getImage() {
        return images.get(estado.ordinal()).get(getIdade() - 1);
    }

    /**
     * Metodo para obter sono atual do bicho virtual
     *
     * @return int
     */
    public int getSono() {
        return sono;
    }

    /**
     * Metodo para obter a felicidade atual do bicho virtual
     *
     * @return int
     */
    public int getFelicidade() {
        return felicidade;
    }

    public Estados getEstado(){
        return this.estado;
    }
    
    /**
     * Metodo para adicionar os Observadores na lista de observadores da classe
     * atual.
     *
     * @param vo
     * @see List
     * @return void
     */
    public void addViewObserver(ViewObserve vo) {
        this.observadores.add(vo);
        notifyValues();
        notifyImage();
    }

    /**
     * Metodo para remover um observador especifico da lista de observadores
     * existente
     *
     * @param vo
     * @see List
     * @return void
     */
    public void removeViewObserver(ViewObserve vo) {
        this.observadores.remove(vo);
    }

    /**
     * Metodo para obter o nivel critico do bicho virtual, atraves dos niveis
     * definidos em "minimo" e "maximo" onde o estado do bicho virtual sera
     * definido. (muito sono, muita fome, triste, sono_fome, normal)
     *
     * @param min
     * @param max
     * @return int
     */
    public int getNivelCritico(){
        
        int nivel = 0;
        Estados novoEstados = null;
        
        if(this.sono > NIVEL_MAX){
            nivel++;
            novoEstados = Estados.SONO;
        }

        if (this.fome > NIVEL_MAX) {
            nivel++;
            novoEstados = Estados.FOME;
        }

        if (this.felicidade < NIVEL_MIN) {
            nivel++;
            novoEstados = Estados.TRISTE;
        }

        if(this.sono > NIVEL_MAX && this.fome > NIVEL_MAX){
            novoEstados = Estados.SONO_FOME;
        }
        
        if(nivel == 3){
            novoEstados = Estados.SAUDE_CRITICA;
        }
        
        if(this.saude < NIVEL_MIN){
            novoEstados = Estados.SAUDE_CRITICA;
        }
        
        if(nivel == 0 && this.felicidade < NIVEL_MAX){
            novoEstados = Estados.NORMAL;
        }
        
        setEstado(novoEstados);
        return nivel;

    }

    /**
     * Metodo para obter o nivel Otimista do bicho virtual, atraves dos niveis
     * "minimo" e "maximo" retornando o estado em nivel de sono e fome e analisando 
     * os estados em niveis de unidade para identificar se o estado é nomal ou
     * feliz.
     *
     * @param min
     * @param max
     * @return int
     */
    public int getNivelOtimo() {

        int nivel = 0;
        
        if (this.sono < NIVEL_MIN) {
            nivel++;
        }

        if (this.fome < NIVEL_MIN) {
            nivel++;
        }
        
        if (this.felicidade > NIVEL_MAX && this.sono < NIVEL_MAX && this.fome < NIVEL_MAX) {
            nivel++;
            setEstado(Estados.FELIZ);
        }

        return nivel;
    }

    /**
     * Metodo responsavel por obter as propriedades do bicho virtual e neste
     * mesmo metodo são inseridos os possiveis estados do bicho virtual
     *
     * @return Properties
     */
    public Properties getPropriedades() {
        Properties propriedades = new Properties();
        propriedades.setProperty("sono", String.valueOf(this.sono));
        propriedades.setProperty("saude", String.valueOf(this.saude));
        propriedades.setProperty("fome", String.valueOf(this.fome));
        propriedades.setProperty("peso", String.valueOf(this.peso));
        propriedades.setProperty("idade", String.valueOf(this.idade));
        propriedades.setProperty("felicidade", String.valueOf(this.felicidade));
        return propriedades;
    }

    /**
     * Metodo que notifica aos obsevadores os valores atuais, para definição de 
     * um estado.
     */
    public void notifyValues(){
        if(this.observadores != null){
            for(ViewObserve vob : observadores){
                vob.notifyValues(saude,getIdade(),peso,sono,felicidade,fome);
            }
        }
    }
    
    /**
     * Metodo que notifica as imagens precisas aos observadores.
     * 
     */
    public void notifyImage(){
        if(this.observadores != null){
            for(ViewObserve vob : observadores){
                vob.notifyImage(getImage());
            }
        }
    }
    
    /**
     * Metodo que notifica os valores em estados criticos do sistema,
     * atravez de contantes pre-definidas que revelam quais são os niveis 
     * maximo e minimos.
     */
    public void notifyMinMax(){
        if(this.observadores != null){
            for(ViewObserve vo : this.observadores){
                vo.notifyMinMax(NIVEL_MIN, NIVEL_MAX);
            }
        }
    }
    
    /**
     * Metodo que faz o bicho virtual representar o estado dormindo, e notifica 
     * os valores aos obeservadores.
     * 
     * @see Enum
     */
    public void dormir(int und) {
        this.sono -= und;
        if(this.sono < MIN_VALUES){
            this.sono = MIN_VALUES;
        }
        
        this.alterar = true;
        setEstado(Estados.DORMIR);
        this.alterar = false;
        
        notifyValues();
        notifyMinMax();
    }
    
    /**
     * Metodo que faz o bicho virtual representar o estado comer, e notifica os 
     * valores aos observadores.
     * 
     * @param und
     */
    public void comer(int und){
        this.fome -= und;
        if(this.fome < MIN_VALUES){
            this.fome = MIN_VALUES;
        }
        
        this.alterar = true;
        setEstado(Estados.COMER);
        this.alterar = false;
        
        notifyValues();
        notifyMinMax();
    }

    /**
     * Metodo que faz o bicho virtual representar o estado brincar.
     * 
     * @see Enum
     */
    public void brincar(int und){
        this.felicidade += und;
        if(this.felicidade > MAX_VALUES){
            this.felicidade = MAX_VALUES;
        }
        
        setEstado(Estados.FELIZ);
        this.alterar = false;
        
        notifyValues();
        notifyMinMax();
    }
    
    /**
     * Metodo que recebe um estado e altera o atual notificando aos observadores.
     * @param e
     */
    public void setEstado(Estados e){
        if(this.alterar){
            if(e != null && !this.estado.equals(e)){
                this.estado = e;
                notifyImage();
            }
        }
    }
    
    /**
     * Metodo que vai verificar a alteração das imagens.
     * @param alterar
     */
    public void setAlteraImage(boolean alterar){
        this.alterar = alterar;
    }
    
    public void brincarLeft(){
        if(this.estado == Estados.FELIZ && !this.alterar || 
                this.estado == Estados.BRINCAR_DIREITA && !this.alterar){
            this.alterar = true;
            setEstado(Estados.BRINCAR_ESQUERDA);
            this.alterar = false;
        }
    }
    
    public void brincarRight(){
        if(this.estado == Estados.FELIZ && !this.alterar || 
                this.estado == Estados.BRINCAR_ESQUERDA && !this.alterar ){
            this.alterar = true;
            setEstado(Estados.BRINCAR_DIREITA);
            this.alterar = false;
        }
    }
    
}
